﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Cofe.Core.Implements;
using Cofe.Core.Utils;

namespace Cofe.Core.Tasks
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class CofeTaskManager : ITaskManager
    {
        #region Constructor

        public CofeTaskManager()
        {
            TaskScheduler = Task.Factory.Scheduler;
            TaskConstructor = new TaskConstructor();
            ThreadStartInterval = TimeSpan.FromSeconds(1);
        }

        #endregion

        #region Methods

        public void AbortAll()
        {
           CofeServices.Logger.Log("CTM: Aborting all running tasks.");
           _taskManagerCts.Cancel();
            
            _taskManagerCts = new CancellationTokenSource();
        }

        private void attachLogging(Task task)
        {
            task.ContinueWith((t) =>
            {
                if (t.IsFaulted)
                    CofeServices.Logger.Log(t.Exception);
            }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler);
        }

        public Task InvokeAsync(PropertyPair ppair, ParameterDic parameters)
        {
            var task = CofeServices.PropertyDictionary.PropertyInvoker.InvokeAsync(ppair, parameters, TaskScheduler);
            attachLogging(task);
            return task;
        }

        public Task<T> InvokeAsync<T>(PropertyPair ppair, ParameterDic parameters)
        {
            var task = CofeServices.PropertyDictionary.PropertyInvoker.InvokeAsync<T>(ppair, parameters, TaskScheduler);
            attachLogging(task);
            return task;
        }

        public async Task StartAsync(Task task)
        {
            if (task.Status != TaskStatus.Running)
                task.Start(TaskScheduler);
            await task;
        }

        public async Task<T> StartAsync<T>(Task<T> task)
        {
            if (task.Status != TaskStatus.Running)
                task.Start(TaskScheduler);
            return await task;
        }

        public void StartRepeat(Action<CancellationToken> action, TimeSpan waitTime, CancellationToken token, int threads = 1)
        {
            Task.Run(() =>
                {                    
                    Action finishedAction = () => 
                    { 
                        CofeServices.Logger.Log("CTM: Repeat Task {0} aborted.", Task.CurrentId); 
                    };
                    if (token != CancellationToken.None)
                    {
                        var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _taskManagerCts.Token);
                        lock (_startedTokenSource)
                            _startedTokenSource.Add(combinedTokenSource);
                        token = combinedTokenSource.Token;
                        finishedAction = () =>
                        {
                            _startedTokenSource.Remove(combinedTokenSource);
                        CofeServices.Logger.Log("CTM: Repeat Task {0} aborted.", Task.CurrentId); 
                        };
                    }

                    for (int i = 1; i <= threads; i++)
                    {
                        var task = TaskUtils.Repeat(waitTime, action, token, (ex) => CofeServices.Logger.Log(ex),
                            () => finishedAction());
                        CofeServices.Logger.Log("CTM: Repeat Task {0} started.", task.Id);
                        Task.Delay(ThreadStartInterval).Wait();
                    }
                });
        }
      

        #endregion

        #region Data

        private CancellationTokenSource _taskManagerCts = new CancellationTokenSource();
        private List<CancellationTokenSource> _startedTokenSource = new List<CancellationTokenSource>();

        #endregion

        #region Public Properties

        public TimeSpan ThreadStartInterval { get; set; }
        public TaskScheduler TaskScheduler { get; private set; }
        public ITaskConstructor TaskConstructor { get; private set; }

        #endregion


    }
}
